package com.example.base.job;


import com.alibaba.fastjson.JSONArray;
import com.example.base.log.LogModel;
import com.example.base.log.LogUtils;
import com.example.base.system.domain.SysJob;
import com.example.base.system.domain.SysJobRecord;
import com.example.base.system.domain.SysLongtext;
import com.example.base.system.service.SysJobRecordService;
import com.example.base.system.service.SysLongtextService;
import com.example.base.utils.ReflectUtils;
import com.example.base.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.EverythingMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;

/**
 * @author Xiongx
 * @version 1.0
 * @date 2021/3/20 19:57
 * @since JDK 1.8
 * 定时任务工具
 */
@Component
@Slf4j
public class QuarztUtils {


    @Autowired
    private Scheduler scheduler;

    @Autowired
    private JobListenerServer jobListenerServer;

    @Autowired
    private SysLongtextService sysLongtextService;

    private static int num=0;

    @Autowired
    private SysJobRecordService sysJobRecordService;

    public JobModel sysJobToJobModel(SysJob sysJob){
        JobModel jobModel=new JobModel();
        jobModel.setId(sysJob.getId());
        jobModel.setJobname(sysJob.getJobname());
        jobModel.setClazz(sysJob.getClazz());
        jobModel.setMethodclazz(sysJob.getMethodclazz());
        jobModel.setMethodname(sysJob.getMethodname());
        jobModel.setType(sysJob.getType());
        jobModel.setCron(sysJob.getCron());
        jobModel.setStatus(sysJob.getStatus());
        jobModel.setGroupName(sysJob.getGroupName());
        jobModel.setParams(sysJob.getParams());
        jobModel.setAnnotationName(sysJob.getAnnotationName());
        jobModel.setAppClass(sysJob.getAppClass());
        jobModel.setAppName(sysJob.getAppName());
        return jobModel;
    }


    public Object executeAnnotationJob(JobModel job) throws InvocationTargetException, IllegalAccessException {
        JobConfig jobConfig= SpringContextUtils.getBean(JobConfig.class);
        String name=job.getAnnotationName();
        if(name!=null){
            Object obj=jobConfig.getJobObjectByAnnotationName(name);
            if(obj!=null){
                JSONArray array=null;
                if(StringUtils.isNotBlank(job.getParams())){
                    array=JSONArray.parseArray(job.getParams());
                }
                Class<?> cls=obj.getClass();
                Method[] methods=cls.getDeclaredMethods();
                Method invokeMethod=methods[0];
                Type[] types=invokeMethod.getGenericParameterTypes();
                Object[] args=new Object[types.length];
                for (int i = 0; i < types.length ; i++) {
                    Type type=types[i];
                    if(array==null){
                        args[i]=null;
                    }else {
                        if(array.size()-1 > i){
                            args[i]=null;
                        }else {
                            args[i]=array.get(i);
                        }
                    }
                }
                LogModel logModel=new LogModel(cls.getName());
                LogUtils.addLogModel(cls.getName(),logModel);

                SysJobRecord sysJobRecord=new SysJobRecord();
                sysJobRecord.setCreatetime(new Date());
                sysJobRecord.setStartDate(new Date());
                sysJobRecord.setJobId(job.getId());
                Long start=System.currentTimeMillis();

                SysLongtext sysLongtext=new SysLongtext();
                sysLongtext.setCreatetime(new Date());
                if(args.length>0){
                    try {
                        invokeMethod.invoke(obj,args);
                        String logText=logModel.getLogStr();
                        sysLongtext.setBigtext(logText);
                        sysJobRecord.setResultStatus(true);
                    }catch (Exception e){
                        StringWriter sw = new StringWriter();
                        PrintWriter pw = new PrintWriter(sw);
                        e.printStackTrace(pw);
                        String exceptionMessage = sw.toString();
                        sysJobRecord.setResultStatus(false);
                        sysLongtext.setBigtext(exceptionMessage);
                    }
                    sysLongtextService.save(sysLongtext);
                    sysJobRecord.setLongtextId(sysLongtext.getId());
                    Long end=System.currentTimeMillis();
                    sysJobRecord.setExecTime(Integer.parseInt(String.valueOf(end - start)));
                    sysJobRecord.setEndDate(new Date());
                    sysJobRecordService.save(sysJobRecord);
                }else {
                    try {
                        invokeMethod.invoke(obj);
                        String logText=logModel.getLogStr();
                        sysLongtext.setBigtext(logText);
                        sysJobRecord.setResultStatus(true);
                    }catch (Exception e){
                        StringWriter sw = new StringWriter();
                        PrintWriter pw = new PrintWriter(sw);
                        e.printStackTrace(pw);
                        String exceptionMessage = sw.toString();
                        sysJobRecord.setResultStatus(false);
                        sysLongtext.setBigtext(exceptionMessage);
                    }
                    sysLongtextService.save(sysLongtext);
                    sysJobRecord.setLongtextId(sysLongtext.getId());

                    Long end=System.currentTimeMillis();
                    sysJobRecord.setExecTime(Integer.parseInt(String.valueOf(end - start)));
                    sysJobRecord.setEndDate(new Date());
                    sysJobRecordService.save(sysJobRecord);
                }
                LogUtils.clear(cls.getName());
            }
        }
        return null;
    }

    public Object execute(JobModel job){

        String clazz= job.getMethodclazz();
        String methodname= job.getMethodname();

        log.info(clazz+"任务开始执行了");
        Object value=null;
        //反射执行
        SysJobRecord sysJobRecord=new SysJobRecord();
        sysJobRecord.setCreatetime(new Date());
        sysJobRecord.setStartDate(new Date());
        sysJobRecord.setJobId(job.getId());
        Long start=System.currentTimeMillis();
        SysLongtext sysLongtext=new SysLongtext();
        sysLongtext.setCreatetime(new Date());

        LogModel logModel=new LogModel(clazz);
        LogUtils.addLogModel(clazz,logModel);
        if(job.getParams()!=null){
            JSONArray jsonArray= JSONArray.parseArray(job.getParams());
            Object [] args=new Object[jsonArray.size()] ;
            for (int i = 0; i < jsonArray.size(); i++) {
                args[i]=jsonArray.get(i);
            }
            try {
                value= ReflectUtils.methodInvoke(clazz,methodname,args);
                sysJobRecord.setResultStatus(true);
                String logText=logModel.getLogStr();
                sysLongtext.setBigtext(logText);
            }catch (Exception e){

                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                e.printStackTrace(pw);
                String exceptionMessage = sw.toString();
                sysJobRecord.setResultStatus(false);
                sysLongtext.setBigtext(exceptionMessage);
            }
            sysLongtextService.save(sysLongtext);
            sysJobRecord.setLongtextId(sysLongtext.getId());
            Long end=System.currentTimeMillis();
            sysJobRecord.setExecTime(Integer.parseInt(String.valueOf(end - start)));
            sysJobRecord.setEndDate(new Date());
            sysJobRecordService.save(sysJobRecord);
        }else {
            try {
                value= ReflectUtils.methodInvoke(clazz,methodname,null);
                sysJobRecord.setResultStatus(true);
                String logText=logModel.getLogStr();
                sysLongtext.setBigtext(logText);
            }catch (Exception e){
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                e.printStackTrace(pw);
                String exceptionMessage = sw.toString();
                sysJobRecord.setResultStatus(false);
                sysLongtext.setBigtext(exceptionMessage);
            }
            sysLongtextService.save(sysLongtext);
            sysJobRecord.setLongtextId(sysLongtext.getId());
            Long end=System.currentTimeMillis();
            sysJobRecord.setExecTime(Integer.parseInt(String.valueOf(end - start)));
            sysJobRecord.setEndDate(new Date());
            sysJobRecordService.save(sysJobRecord);
        }
        return value;
    }

    /**
     * 添加rest调用
     */
    public void addQuartz(JobModel job){
        String clazz=job.getClazz();
        log.info("开始添加任务了 {}",clazz);
        try {
            if(num==0){
                num++;
                scheduler.clear();
            }

            //添加执行监听器
            scheduler.getListenerManager().addJobListener(jobListenerServer, EverythingMatcher.allJobs());

            scheduler.start();
            JobDetail jobDetail;
            JobDataMap jobDataMap=new JobDataMap();
            jobDataMap.put("sysjob",job);

            jobDetail= JobBuilder.newJob(getClass(clazz).getClass()).withIdentity(clazz,job.getGroupName())
                        .usingJobData(jobDataMap)
                        .build();

            // 表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(clazz,job.getGroupName()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail,trigger);
            log.info(job.getJobname()+"任务添加成功");
        }catch (Exception e){
            log.info(job.getJobname()+"添加失败 失败信息如下 "+e.getMessage());
        }
    }

    public void startAll(){
        try {
            scheduler.resumeAll();
        }catch (Exception e){
            log.info("启动失败信息如下"+e.getMessage());
            e.printStackTrace();
        }
        log.info("启动所有任务成功");
    }

    /**
     * 暂停所有任务
     */
    public void stopAll(){
        try {
            scheduler.pauseAll();
            log.info("暂停所有任务成功");
        }catch (Exception e){
            e.getMessage();
        }
    }


    /**
     * 执行一次
     * @param
     */
    public boolean execOne(JobModel sysJob){
        try {
            if(sysJob.getType().equals(QuartzEnum.METHOD.getName())){
                execute(sysJob);
            }else if(sysJob.getType().equals(QuartzEnum.ANNOTATION.getName())){
                executeAnnotationJob(sysJob);
            }
            log.info("执行一次任务成功 {},任务名称{},任务类型{}",sysJob.getClazz(),sysJob.getJobname(),sysJob.getType());
            return true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }



    /**
     * 任务暂停
     */
    public void stopQuarzt(JobModel sysJob) {
        try {
            String clazz= sysJob.getClazz();
            String group= sysJob.getGroupName();
            scheduler.pauseJob(JobKey.jobKey(clazz,group));
            log.info(sysJob.getJobname()+"任务暂停成功");
        }catch (Exception e){
            log.info(sysJob.getJobname()+"任务暂停失败");
            e.printStackTrace();
        }
    }



    /**
     * 任务恢复
     */
    public void startQuarzt(JobModel sysJob) {
        String clazz= sysJob.getClazz();
        try {
            String group= sysJob.getGroupName();
            scheduler.resumeJob(JobKey.jobKey(clazz,group));
            System.out.println("任务启动成功");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("任务启动失败");
        }
    }

    public void runOnce(String jobName, String groupName) {
        try {
            scheduler.triggerJob(JobKey.jobKey(jobName, groupName));
        } catch (SchedulerException e) {
            log.error("立即运行一次定时任务失败", e);
        }
    }


    /**
     * 任务删除
     */
    public void deleteQuarzt(JobModel sysJob) {
        try {
            String group= sysJob.getGroupName();
            String clazz= sysJob.getClazz();
            scheduler.pauseTrigger(TriggerKey.triggerKey(clazz,group));  //暂停
            scheduler.unscheduleJob(TriggerKey.triggerKey(clazz,group));
            scheduler.deleteJob(JobKey.jobKey(clazz,group));
            sysJob.setStatus("3");
        }catch (Exception e){
            e.printStackTrace();
            sysJob.setStatus("1");
        }
        sysJob.setStatus("0");
    }



    /**
     * 返回类路径
     * @param
     * @return
     */
    private String returnClass(JobModel sysJob){
        String clazz= sysJob.getClazz();
        String type= sysJob.getType();
        return clazz;
    }


    /**
     * 更改任务
     */
    public void updataQuarzt(JobModel job) {
        try {
            String group=job.getGroupName();
            String cron=job.getCron();
            String clazz=job.getClazz();

            TriggerKey triggerKey = TriggerKey.triggerKey(clazz,group);
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }catch (Exception e){
            e.printStackTrace();
        }

    }


    static QuartzJobBean getClass(String classname) throws Exception {
        Class<?> class1 = Class.forName(classname);
        return (QuartzJobBean) class1.newInstance();
    }


    /**
     * 执行分钟
     * @param time
     * @return
     */
    private String getCronMinute(String time){
        return "0 */"+time+" * * * ?";
    }

    /**
     * 执行秒
     * @param time
     * @return
     */
    private String getCronSecond(String time){
        return "0/"+time+" * * * * ?";
    }

    /**
     * 执行小时
     * @param time
     * @return
     */
    private String getCronHour(String time){
        return "0 0 */"+time+" * * ?";
    }

    /**
     * 每天固定几点执行
     * @param time
     * @return
     */
    private String getCronHourTimeDay(String time){
        return "0 0 "+time+" * * ?";
    }

    /**
     * 每月的某日 某点 执行
     * @param day  当月的某一天
     * @param time  当天的某一小时
     * @return
     */
    private String getCronDayTimeMouth(String day,String time){
        return "0 0 "+day+" "+time+" * ?";
    }


}
